package com.starview.star.code.repository;

import com.starview.platform.data.type.Location;
import com.starview.platform.data.value.ResourceURI;
import com.starview.platform.resource.ResourceException;
import com.starview.platform.resource.Resources;
import com.starview.platform.resource.catalog.Catalog;
import com.starview.platform.resource.catalog.CatalogException;
import com.starview.platform.resource.catalog.CatalogUtils;
import com.starview.star.CompileDriver;
import com.starview.star.StarCompiler;
import com.starview.star.StarMain;
import com.starview.star.code.Manifest;
import com.starview.star.compiler.ErrorReport;
import com.starview.star.compiler.ast.IAbstract;
import com.starview.star.compiler.cafe.Names;
import com.starview.star.compiler.cafe.compile.CafeManifest;
import com.starview.star.compiler.cafe.compile.ClassRoot;
import com.starview.star.compiler.sources.MetaRules;
import com.starview.star.compiler.util.StringUtils;

import java.io.File;

/**
 * The RepositoryManager helps the Star compiler to figure out the details of compiling and importing code
 *
 * @author fgm
 */
public class RepositoryManager{
  public static final String METAENTRY = "metaRulesEntry";
  public static final String MANIFEST = "starManifest";
  public static final String COMPILED = "compiledEntry";

  public static CodeCatalog locatePackage(CodeRepository repository, ResourceURI uri) throws ResourceException,
          CatalogException{
    RepositoryNode repoNode = repository.findNode(uri);

    if (repoNode != null) {
      CodeTree code = repoNode.getCode();
      if (code instanceof CodeCatalog)
        return (CodeCatalog) code;
      else if (!(code instanceof CodeCatalog))
        throw new CatalogException("repository contains invalid entry for " + uri);
      else
        return ((CodeCatalog) code);
    } else
      return null;
  }

  public static boolean isPreambleURI(ResourceURI uri){
    return uri.getScheme().equals(Resources.STDSCHEME);
  }

  public static CodeCatalog locatePackage(CodeRepository repository, String name, Catalog catalog)
          throws CatalogException, ResourceException{
    ResourceURI uri = catalog.resolve(name);
    if (uri != null)
      return locatePackage(repository, uri);
    else
      return null;
  }

  public static Catalog lookForCatalog(ResourceURI uri, Catalog fallback, CodeRepository repository){
    try {
      return CatalogUtils.parseCatalog(uri, fallback);
    } catch (Exception e) {
      return fallback;
    }
  }

  public static MetaRules locateMeta(CodeRepository repository, ResourceURI uri) throws ResourceException,
          CatalogException, RepositoryException{
    CodeCatalog imported = locatePackage(repository, uri);
    if (imported != null) {
      CodeTree importMeta = imported.resolve(METAENTRY, MetaRules.EXTENSION);
      if (importMeta instanceof MetaRules)
        return (MetaRules) importMeta;
    }

    return null;
  }

  public static Manifest locateStarManifest(CodeRepository repository, IAbstract pkgName, Catalog catalog,
                                            ErrorReport errors) throws ResourceException, CatalogException,
          RepositoryException{
    CodeCatalog imported = locatePackage(repository, CompileDriver.uriOfPkgRef(pkgName, catalog));
    if (imported != null) {
      CodeTree importMeta = imported.resolve(MANIFEST, Manifest.EXTENSION);
      if (importMeta instanceof Manifest)
        return (Manifest) importMeta;
    }
    return null;
  }

  public static Manifest locateStarManifest(CodeRepository repository, ResourceURI uri) throws ResourceException,
          CatalogException, RepositoryException{
    CodeCatalog imported = locatePackage(repository, uri);
    if (imported != null) {
      CodeTree importMeta = imported.resolve(MANIFEST, Manifest.EXTENSION);
      if (importMeta instanceof Manifest)
        return (Manifest) importMeta;
    }
    return null;
  }

  public static CafeManifest locateCafeManifest(CodeRepository repository, ResourceURI uri){
    CodeTree codeCatalog = repository.findCode(uri);

    if (codeCatalog instanceof CodeCatalog) {
      try {
        CodeTree manifestEntry = ((CodeCatalog) codeCatalog).resolve(Names.CAFE_MANIFEST, CafeManifest.EXTENSION);
        if (manifestEntry instanceof CafeManifest)
          return (CafeManifest) manifestEntry;
      } catch (RepositoryException e) {
        return null;
      }
    }

    return null;
  }

  public static ClassRoot locateClassRoot(CodeRepository repository, Location loc, ResourceURI uri, ErrorReport errors){
    CodeTree codeCatalog = repository.findCode(uri);

    if (codeCatalog instanceof CodeCatalog) {
      try {
        CodeTree entry = ((CodeCatalog) codeCatalog).resolve(Names.CLASS_ROOT, ClassRoot.EXTENSION);
        if (entry instanceof ClassRoot)
          return (ClassRoot) entry;
      } catch (RepositoryException e) {
        errors.reportError("cannot access " + uri + "\nbecause " + e.getMessage(), loc);
      }
    } else
      errors.reportError("cannot access " + uri, loc);

    return null;
  }

  public static CodeRepository setupStandardRepository(ClassLoader defltLoader){
    ErrorReport errors = new ErrorReport();

    try {
      CodeRepository tgtRepository;
      if (!StringUtils.isTrivial(StarMain.TARGET_DIR))
        tgtRepository = new DirectoryRepository(new File(StarMain.TARGET_DIR), false, true, defltLoader, errors);
      else
        tgtRepository = new CodeRepositoryImpl(defltLoader, true, errors);

      if (!StringUtils.isTrivial(StarCompiler.STAR_PATH))
        tgtRepository = new CompositeRepository(StarCompiler.STAR_PATH, tgtRepository, false, defltLoader, errors);

      return tgtRepository;
    } catch (RepositoryException e) {
      errors.reportWarning(e.getMessage());
      return null;
    } finally {
      if (!errors.isErrorFree()) {
        System.err.println(errors);
        System.exit(1);
      }
    }
  }
}
